//=============================================================================
// System  : Sandcastle Help File Builder Components
// File    : IndexLinkExtenderComponent.cs
// Author  : Martin Darilek
// Updated : 29/03/2007
// Compiler: Microsoft Visual C#
//
// This file contains a build component that is used to search for <mshelp:link> 
// tags and add configured help namespace attribute into them.   
// 
// !!! Note that this component must be started at the end of build process before
// last step -> saving output into file.
//
// Version	Date		Who		Comments
// ============================================================================
// 1.0.0.0	22.01.2007	MaD		Created the code
// 1.0.1.0	06.06.2007	MaD		Configuration settings restructuralization
//								Compatible with Sandcastle June 2007 CTP
//=============================================================================

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.XPath;

using Microsoft.Ddue.Tools;

using Darilek.SandcastleExtensions.Utilities;

namespace Darilek.SandcastleExtensions.Components
{
	/// <summary>
	/// Build component that is used to search for &lt;mshelp:link&gt; tags 
	/// and add configured help namespace attribute into them.
	/// </summary>
	/// <remarks>
	/// This component must be started at the end of build process
	/// (before last step - saving output into file).
	/// </remarks>
	public sealed class IndexLinkNamespaceComponent : BuildComponent
	{
		// sorted list with namespace mappings
		private SortedList<string, string> _namespaceList = new SortedList<string, string>();

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="assembler">>Reference to the build assembler</param>
		/// <param name="configuration">The configuration information</param>
		/// <exception cref="ConfigurationErrorsException">This is thrown if
		/// an error is detected in the configuration.</exception>
		public IndexLinkNamespaceComponent(BuildAssembler assembler, XPathNavigator configuration)
			: base(assembler, configuration)
		{

			base.WriteMessage(MessageLevel.Info, InfoHelper.GetFormattedInfo("Index Link Namespace Component"));

			// read configuration
			XPathNodeIterator iterator = configuration.Select("mappings/mapping");
			XPathNavigator navigator;
			string managedNamespace;
			string helpNamespace;

			while (iterator.MoveNext())
			{
				navigator = iterator.Current;
				managedNamespace = navigator.GetAttribute("managedNamespace", String.Empty);
				helpNamespace = navigator.GetAttribute("helpNamespace", String.Empty);

				// check attributes
				if (String.IsNullOrEmpty(managedNamespace) || String.IsNullOrEmpty(helpNamespace))
					throw new ConfigurationErrorsException("You must specify 'managedNamespace' and 'helpNamespace' attributes on the <mapping> element.");
				
				_namespaceList.Add(managedNamespace, helpNamespace);
			}

			base.WriteMessage(MessageLevel.Info, String.Format("Loaded {0} namespace mapping(s)", _namespaceList.Keys.Count));

		}

		#endregion

		#region overrides

		/// <summary>
		/// Index links modification implementation.
		/// </summary>
		/// <param name="document">The XML document with which to work.</param>
		/// <param name="key">The key (member name) of the item being
		/// documented.</param>
		public override void Apply(XmlDocument document, string key)
		{
			XmlNodeList linkList;
			XmlNamespaceManager mgr = new XmlNamespaceManager(document.NameTable);
			XmlAttribute namespaceAttribute;
			XmlAttribute keyword;
			string origNamespace;

			mgr.AddNamespace("mshelp", "http://msdn.microsoft.com/mshelp");

			// link nodes must be selected with XmlNamespaceManager support
			linkList = document.SelectNodes("//mshelp:link", mgr);

			base.WriteMessage(MessageLevel.Info, String.Format("{0} link nodes found", linkList.Count));

			foreach (XmlNode node in linkList)
			{
				// try to read "keywords" attribute
				keyword = node.Attributes["keywords"];

				if (keyword == null || String.IsNullOrEmpty(keyword.Value))
					continue;

				// remove first two characters, so we get clear full object namespace declaration
				// ie. T:System.String -> System.String
				origNamespace = keyword.Value.Substring(2);

				// just check ...
				if (String.IsNullOrEmpty(origNamespace))
					continue;

				string newNamespace = origNamespace;
				namespaceAttribute = null;

				// main process ...
				// we try find value in declaration list by <helpNamespace> key.
				// If value not found, remove text after last separator ('.') 
				// and repeat search process again ...   until any separator or key found
				while (true)
				{
					if (_namespaceList.ContainsKey(newNamespace))
					{
						namespaceAttribute = document.CreateAttribute("namespace");
						namespaceAttribute.Value = _namespaceList[newNamespace];
						node.Attributes.Append(namespaceAttribute);
						break;
					}

					if (newNamespace.IndexOf(".") == -1)
						break;

					// remove text after last namespace separator
					newNamespace = newNamespace.Substring(0, newNamespace.LastIndexOf("."));

				}
			}
		}

		#endregion
	}
}
